# from xv6-riscv:
# code used to switch context between user and kernel space
#
# this code is mapped at the same virtual address
# (TRAMPOLINE) in user and kernel space so that
# it continues to work when it switches page tables.
#
# note: code size here should not be larger than a page,
#       and kernel.ld will align the page for trampsec section
#
# diff: swap the region of userret and uservec,
#       because rust can not call userret directly then.
#       otherwise rust code need to add some address to the function pointer,
#       which is not allowed.

.section trampsec
.globl trampoline
trampoline:

.globl uservec
uservec:
    # user_trap_ret() sets stvec to point here, so
    # traps from user space start here,
    # in supervisor mode, but with a
    # user page table.
    #
    # sscratch points to where the process's p->tf is
    # mapped into user space, at TRAPFRAME.
    #
        
	# swap a0 and sscratch
    # so that a0 is TRAPFRAME
    csrrw a0, sscratch, a0

    # save the user registers in TRAPFRAME
    sd ra, 40(a0)
    sd sp, 48(a0)
    sd gp, 56(a0)
    sd tp, 64(a0)
    sd t0, 72(a0)
    sd t1, 80(a0)
    sd t2, 88(a0)
    sd s0, 96(a0)
    sd s1, 104(a0)
    sd a1, 120(a0)
    sd a2, 128(a0)
    sd a3, 136(a0)
    sd a4, 144(a0)
    sd a5, 152(a0)
    sd a6, 160(a0)
    sd a7, 168(a0)
    sd s2, 176(a0)
    sd s3, 184(a0)
    sd s4, 192(a0)
    sd s5, 200(a0)
    sd s6, 208(a0)
    sd s7, 216(a0)
    sd s8, 224(a0)
    sd s9, 232(a0)
    sd s10, 240(a0)
    sd s11, 248(a0)
    sd t3, 256(a0)
    sd t4, 264(a0)
    sd t5, 272(a0)
    sd t6, 280(a0)

	# save the user a0 in p->tf->a0
    csrr t0, sscratch
    sd t0, 112(a0)

    # save the user program counter
    csrr t0, sepc
    sd t0, 24(a0)

    # restore kernel stack pointer from p->tf->kernel_sp
    ld sp, 8(a0)

    # make tp hold the current hartid, from p->tf->kernel_hartid
    ld tp, 32(a0)

    # load the address of user_trap(), p->tf->kernel_trap
    ld t0, 16(a0)

    # restore kernel page table from p->tf->kernel_satp
    ld t1, 0(a0)
    csrw satp, t1
    sfence.vma zero, zero

    # a0 is no longer valid, since the kernel page
    # table does not specially map p->tf.

    # jump to user_trap(), which does not return
    jr t0

.align 4
.globl userret
userret:
    # userret(TRAPFRAME, pagetable)
    # switch from kernel to user.
    # usertrapret() calls here.
    # a0: TRAPFRAME, in user page table.
    # a1: user page table, for satp.

    # switch to the user page table.
    csrw satp, a1
    sfence.vma zero, zero

    # put the saved user a0 in sscratch, so we
    # can swap it with our a0 (TRAPFRAME) in the last step.
    ld t0, 112(a0)
    csrw sscratch, t0

    # restore all but a0 from TRAPFRAME
    ld ra, 40(a0)
    ld sp, 48(a0)
    ld gp, 56(a0)
    ld tp, 64(a0)
    ld t0, 72(a0)
    ld t1, 80(a0)
    ld t2, 88(a0)
    ld s0, 96(a0)
    ld s1, 104(a0)
    ld a1, 120(a0)
    ld a2, 128(a0)
    ld a3, 136(a0)
    ld a4, 144(a0)
    ld a5, 152(a0)
    ld a6, 160(a0)
    ld a7, 168(a0)
    ld s2, 176(a0)
    ld s3, 184(a0)
    ld s4, 192(a0)
    ld s5, 200(a0)
    ld s6, 208(a0)
    ld s7, 216(a0)
    ld s8, 224(a0)
    ld s9, 232(a0)
    ld s10, 240(a0)
    ld s11, 248(a0)
    ld t3, 256(a0)
    ld t4, 264(a0)
    ld t5, 272(a0)
    ld t6, 280(a0)

    # restore user a0, and save TRAPFRAME in sscratch
    csrrw a0, sscratch, a0
    
    # return to user mode and user pc.
    # user_trap_ret() set up sstatus and sepc.
    sret
